package annotool.extract;

import mpicbg.imagefeatures.*;
import ij.gui.*;
import ij.*;
import ij.process.*;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Vector;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.Writer;

import javax.imageio.ImageIO;

import annotool.Visualization;
import annotool.clustering.Hough;

public class SIFTExtractor
{
	private static int coordinate = 2;

	private static int no_feats = 50;

	public int featureSize;		// size of the feature
	// steps
	private static int steps = 5;
	// initial sigma
	private static float initial_sigma = 1.6f;
	private static float scale = 1.0f;

	// feature descriptor size
	private static int fdsize = 4;
	// feature descriptor orientation bins
	private static int fdbins = 8;
	// size restrictions for scale octaves, use octaves < max_size and > min_size only
	private static int min_size = 16;
	private static int max_size = 1024;
	// closest/next closest neighbour distance ratio
	private annotool.io.DataInputDynamic datainput;
    protected float[][] inputFeatures = null;
	protected ArrayList<byte[]> data;
	   int[] totalwidth;
	   int[] totalheight;
	   int length;
	   //ImagePlus[] data1;
   Writer outputfile = null;
   //hough parameters
   private static int hough_Th, hough_Scale, hough_Orie, hough_Loc;
   //
	

	/**
	 * Set true to double the size of the image by linear interpolation to
	 * ( with * 2 + 1 ) * ( height * 2 + 1 ).  Thus we can start identifying
	 * DoG extrema with $\sigma = INITIAL_SIGMA / 2$ like proposed by
	 * \citet{Lowe04}.
	 * 
	 * This is useful for images scmaller than 1000px per side only. 
	 */ 
	private static boolean upscale = false;
	
	//public SIFTExtractor(annotool.io.DataInputDynamic datainput, ImagePlus[] data1)
	public SIFTExtractor(annotool.io.DataInputDynamic datainput,int steps,float scale, float initial_sigma,int fdsize, int fdbins, int min_size, 
			int max_size, boolean upscale, int hough_Th, int hough_Scale, int hough_Orie, int hough_Loc)
	{
		this.datainput = datainput;
		//this.data1 = data1;
		data = datainput.getData();
		length = datainput.getLength();
		totalwidth = datainput.getWidthList();
		totalheight = datainput.getHeightList();
		SIFTExtractor.steps = steps;
		SIFTExtractor.scale = scale;
		SIFTExtractor.initial_sigma = initial_sigma;
		SIFTExtractor.fdbins = fdbins;
		SIFTExtractor.fdsize = fdsize;
		SIFTExtractor.min_size = min_size;
		SIFTExtractor.max_size = max_size;
		SIFTExtractor.upscale = upscale;
		SIFTExtractor.hough_Loc = hough_Loc;
		SIFTExtractor.hough_Orie = hough_Orie;
		SIFTExtractor.hough_Scale = hough_Scale;
		SIFTExtractor.hough_Th = hough_Th;
		//this.inputFeatures = datainput.getSiftData();
	}
	public SIFTExtractor(Writer outputfile, annotool.io.DataInputDynamic datainput,int steps, float scale, float initial_sigma,int fdsize, int fdbins, int min_size, 
			int max_size, boolean upscale, int hough_Th, int hough_Scale, int hough_Orie, int hough_Loc)
	{
		this.outputfile = outputfile;
		this.datainput = datainput;
		//this.data1 = data1;
		data = datainput.getData();
		length = datainput.getLength();
		totalwidth = datainput.getWidthList();
		totalheight = datainput.getHeightList();
		SIFTExtractor.steps = steps;
		SIFTExtractor.scale = scale;
		SIFTExtractor.initial_sigma = initial_sigma;
		SIFTExtractor.fdbins = fdbins;
		SIFTExtractor.fdsize = fdsize;
		SIFTExtractor.min_size = min_size;
		SIFTExtractor.max_size = max_size;
		SIFTExtractor.upscale = upscale;
		SIFTExtractor.hough_Loc = hough_Loc;
		SIFTExtractor.hough_Orie = hough_Orie;
		SIFTExtractor.hough_Scale = hough_Scale;
		SIFTExtractor.hough_Th = hough_Th;
		//this.inputFeatures = datainput.getSiftData();
	}
	public float[][] run( String args )
	{
	//	Runtime r = Runtime.getRuntime();
		float[][] features = new float[length][];
		inputFeatures  = new float[length][]; //In Matlab, an 50*100 image has 5050 features due to rounding.

		if ( IJ.versionLessThan( "1.37i" ) ) return null;
		
		for(int i = 0 ; i < length ; i++)
		{
		inputFeatures[i] = new float[totalwidth[i]*totalheight[i]];
		//ImagePlus imp = data1[i];
        getFeatureOfOneImage(data.get(i), inputFeatures[i], totalheight[i], totalwidth[i]);

		//if ( imp == null )  { System.err.println( "There are no images open" ); return null; }

		GenericDialog gd = new GenericDialog( "SIFT" );
		gd.addNumericField( "minimum_Features to select :", no_feats, 50 );
		gd.addNumericField( "No of Grids :", coordinate, 4 );
		if ( gd.wasCanceled() ) return null;
		no_feats = ( int )gd.getNextNumber();
		coordinate = ( int )gd.getNextNumber();
		
		//ImageProcessor ip1 = imp.getProcessor().convertToFloat();
		//ImageProcessor ip2 = imp.getProcessor().duplicate().convertToRGB();
		
		Vector< Feature > fs1;
		
		FloatArray2DSIFT sift = new FloatArray2DSIFT( fdsize, fdbins );
		//FloatArray2D fa1 = ImageArrayConverter.ImageToFloatArray2D( ip1 );
		FloatArray2D fa = new FloatArray2D(inputFeatures[i], totalwidth[i], totalheight[i]);

		//System.out.println("the lenghtsof fa: "+fa.data.length+" and fa1 is : "+fa1.data.length);
		//for(int a = 0; a < fa.data.length; a++)
		//{
		//	System.out.println("the fa is : "+fa.data[a]+" and of fa1 is : "+inputFeatures[i][a]);
		//}
		Filter.enhance( fa, scale );
		
		float[] initial_kernel;
		
		if ( upscale )
		{
			FloatArray2D fat = new FloatArray2D( fa.width * 2 - 1, fa.height * 2 - 1 ); 
			FloatArray2DScaleOctave.upsample( fa, fat );
			fa = fat;
			fat = null;
			initial_kernel = Filter.createGaussianKernel( ( float )Math.sqrt( initial_sigma * initial_sigma - 1.0 ), true );
		}
		else
			initial_kernel = Filter.createGaussianKernel( ( float )Math.sqrt( initial_sigma * initial_sigma - 0.25 ), true );
		
		fa = Filter.convolveSeparable( fa, initial_kernel, initial_kernel );
		
		long start_time = System.currentTimeMillis();

		System.out.print( (i+1)+" :processing SIFT ..." );
		sift.init( fa, steps, initial_sigma, min_size, max_size );
		fa = null;
		fs1 = sift.run( max_size );
		Collections.sort( fs1 );
		System.out.println( " took " + ( System.currentTimeMillis() - start_time ) + "ms" );
		
		System.out.println( fs1.size() + " SIFT features identified and processed" );
				//for(Feature f: fs1)
					//IJ.log("orientation "+f.orientation+" scale: "+f.scale+" loc "+f.location[0]+" loc2: "+f.location[1]);

		if(outputfile != null)
	     {
	    	 try{
	    		 outputfile.write((i+1)+" :processing SIFT ...");
	    		 outputfile.write( " took " + ( System.currentTimeMillis() - start_time ) + "ms"+"\n");
	    		 outputfile.write(fs1.size() + " SIFT features identified and processed"+"\n" );
					outputfile.flush();
				}catch(java.io.IOException e)
				{ 
					System.out.println("Writing to output file failed.");
				}
	    	 
	     }
		Float[] floatfeatures;
		ArrayList<Float> arrayfloatfeatures = new ArrayList<Float>();

		for(int initial = 0 ; initial < fs1.size() ; initial++)
		{			
			floatfeatures = fs1.get(initial).toFloats();
			this.featureSize = floatfeatures.length;
			//System.out.println(this.featureSize);
			for(int floatcount = 0; floatcount < floatfeatures.length; floatcount++)
			{
				arrayfloatfeatures.add(floatfeatures[floatcount]);
			}

		}
		float[] allfeatures1d = new float[arrayfloatfeatures.size()];
		for(int featurecount = 0; featurecount < arrayfloatfeatures.size();featurecount++)
		{
			allfeatures1d[featurecount] = arrayfloatfeatures.get(featurecount).floatValue();
		}

		features[i] = allfeatures1d;
		//visulalization
		/*BufferedImage buf;
		boolean isCreated = new File("/home/divakaruni/Desktop/images/New_samples_for_HOUGH>2/resul/bmp/SIFT_Extracted/").mkdirs();
		
		Visualization visual = new Visualization(datainput.getImagePlus(i), features[i]); 
		buf = visual.showSave(1, Color.blue);
		File file;
		
		file = new File("/home/divakaruni/Desktop/images/New_samples_for_HOUGH>2/resul/bmp/SIFT_Extracted", +i+".jpg");
		try {
			ImageIO.write(buf, "jpg", file);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}*/
		
		//viz end
	//	r.gc();	//garbage collector
		fs1 = null;
		floatfeatures = null;
		arrayfloatfeatures=null;
		allfeatures1d = null;
		Hough h = null;
		if(outputfile != null)
	     {
		h = new Hough(outputfile, features[i], this.featureSize, totalheight[i], totalwidth[i], hough_Th, hough_Scale, hough_Orie, hough_Loc);
	     }
		else
		{
			h = new Hough(features[i], this.featureSize, totalheight[i], totalwidth[i], hough_Th, hough_Scale, hough_Orie, hough_Loc);	
		}
		
		features[i] = h.H();
	//	r.gc();	//garbage collector
		h = null;
		}
				
		return features;
		
	}
	public int getFeatureSize()
	{
		return this.featureSize;
	}
	protected void getFeatureOfOneImage(byte[] data, float[] feature, int totalheight, int totalwidth)
	   {
		    //copy data to feature,
		    //toFloat() of ColorProcessor does the same loop
		int count = 0;
		for ( int y = 0; y < totalheight; y++ )
			for ( int x = 0; x < totalwidth; x++ )
		       feature[count] = (float)data[count++];
	   }
	
}
